package com.wl.cloud.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.collect.Lists;
import com.wl.cloud.core.dto.DataStoreDTO;
import com.wl.cloud.core.utils.PageUtils;
import com.wl.cloud.security.component.DynamicSecurityMetadataSource;
import com.wl.cloud.security.utils.JwtTokenUtil;
import com.wl.cloud.system.dao.*;
import com.wl.cloud.system.domain.*;
import com.wl.cloud.system.service.SysRoleMenuService;
import com.wl.cloud.system.service.SysRoleResourceService;
import com.wl.cloud.system.service.SysRoleService;
import com.wl.cloud.system.service.SysUserRoleService;
import com.wl.cloud.system.support.dto.RoleRightsDTO;
import com.wl.cloud.system.support.dto.SysRoleDTO;
import com.wl.cloud.system.support.dto.query.SysRoleQueryDTO;
import com.wl.cloud.system.support.utils.ConstantSystem;
import com.wl.cloud.system.support.vo.SysRoleVO;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 角色表 服务实现类
 *
 * @author wanglin
 * @since 2023-09-18
 */
@Service
public class SysRoleServiceImpl implements SysRoleService {

    @Autowired
    private SysRoleMapper sysRoleMapper;
    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private SysMenuMapper menuMapper;
    @Autowired
    private SysRoleMenuMapper roleMenuMapper;
    @Autowired
    private SysRoleMenuService roleMenuService;
    @Autowired
    private SysResourceMapper resourceMapper;

    @Autowired
    private SysUserRoleService userRoleService;
    @Autowired
    private SysRoleResourceService roleResourceService;
    @Autowired
    private SysRoleResourceMapper roleResourceMapper;

    @Autowired
    private DynamicSecurityMetadataSource dynamicSecurityMetadataSource;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;


    @Transactional(readOnly = true)
    @Override
    public DataStoreDTO<SysRoleVO> page(Pageable pageable, SysRoleQueryDTO queryDto) {
        boolean isSuperFlag = jwtTokenUtil.isSuperAdmin();
        QueryWrapper<SysRole> queryWrapper = this.buildQuery(queryDto);
        if (!isSuperFlag) {
            queryWrapper.lambda().notIn(SysRole::getCode, Arrays.asList(ConstantSystem.SUPER_ADMIN, ConstantSystem.ADMIN));
        }
        Page<SysRole> page = PageUtils.transferPage(pageable);
        Page<SysRole> result = this.sysRoleMapper.selectPage(page, queryWrapper);
        return new DataStoreDTO(result.getTotal(), this.transferVo(result.getRecords()));
    }

    @Transactional(readOnly = true)
    @Override
    public List<SysRoleVO> list(Sort sort, SysRoleQueryDTO queryDto) {
        boolean isSuperFlag = jwtTokenUtil.isSuperAdmin();
        QueryWrapper<SysRole> queryWrapper = this.buildQuery(queryDto);
        if (!isSuperFlag) {
            queryWrapper.lambda().notIn(SysRole::getCode, Arrays.asList(ConstantSystem.SUPER_ADMIN, ConstantSystem.ADMIN));
        }
        PageUtils.transferSort(queryWrapper, sort);
        return this.transferVo(this.sysRoleMapper.selectList(queryWrapper));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void save(SysRoleDTO dto) {
        //唯一性验证 账号,name
        List<SysRole> roleList = validateRepeat(dto.getName(), dto.getCode());
        if (!roleList.isEmpty()) {
            roleList.stream().forEach(e -> {
                Assert.isNull(dto.getCode().equals(e.getCode()), "角色编号: " + dto.getCode() + "已存在");
                Assert.isNull(dto.getName().equals(e.getName()), "角色名称: " + dto.getName() + "已存在");
            });
        }

        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            //验证
            List<SysMenu> menuList = menuMapper.selectBatchIds(dto.getMenuIds());
            Assert.isTrue(menuList.size() == dto.getMenuIds().size(), "所选菜单包含不存在的ID");
        }
        dto.setId(null);
        SysRole roleEntity = this.transferEntity(null, dto);
        sysRoleMapper.insert(roleEntity);
        //若菜单不为空，则为该角色分配菜单
        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            List<SysRoleMenu> roleMenuList = dto.getMenuIds().stream().map(e -> new SysRoleMenu(roleEntity.getId(), e)).collect(Collectors.toList());
            roleMenuMapper.insertBatch(roleMenuList);
        }
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void update(SysRoleDTO dto) {
        Assert.hasText(dto.getId(), "id不能为空");
        SysRole entity = sysRoleMapper.selectById(dto.getId());
        //3 - 存在性验证
        Assert.notNull(entity, "找不到id为： " + dto.getId() + " 的记录");
        //4 - 唯一性验证 code
//        SysRole roleByCode = checkByCode(dto.getCode());
//        if (Objects.nonNull(roleByCode)) {
//            Assert.isTrue(roleByCode.getId().equals(dto.getId()), "角色编号: " + dto.getCode() + "已存在");
//        }
//        //4 - 唯一性验证 name
//        SysRole roleByName = checkByName(dto.getName());
//        if (Objects.nonNull(roleByName)) {
//            Assert.isTrue(roleByName.getId().equals(dto.getId()), "角色名称: " + dto.getName() + "已存在");
//        }

        List<SysRole> roleList = validateRepeat(dto.getName(), dto.getCode());
        if (!roleList.isEmpty()) {
            roleList.stream().forEach(e -> {
                if (dto.getCode().equals(e.getCode())) {
                    Assert.isTrue(e.getId().equals(dto.getId()), "角色编号: " + dto.getCode() + "已存在");
                }
                if (dto.getName().equals(e.getName())) {
                    Assert.isTrue(e.getId().equals(dto.getId()), "角色名称: " + dto.getName() + "已存在");
                }
            });
        }
        sysRoleMapper.updateById(this.transferEntity(entity, dto));

        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            //验证
            List<SysMenu> menuList = menuMapper.selectBatchIds(dto.getMenuIds());
            Assert.isTrue(menuList.size() == dto.getMenuIds().size(), "所选菜单包含不存在的ID");
        }
        //删除之前全部
        roleMenuMapper.delete(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, dto.getId()));
        //若菜单不为空，则为该角色分配菜单
        if (CollUtil.isNotEmpty(dto.getMenuIds())) {
            List<SysRoleMenu> roleMenuList = dto.getMenuIds().stream().map(e -> new SysRoleMenu(dto.getId(), e)).collect(Collectors.toList());
            roleMenuMapper.insertBatch(roleMenuList);
        }
    }

    /**
     * 思路：
     * 1.删除用户-角色中间表,若该角色绑定了用户的，则不能删除，只有没有绑定用户的角色才能删除，角色对应的数据；
     * 2.删除角色-资源中间表，角色对应的数据；
     * 3.删除角色
     *
     * @param ids
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void delete(Set<String> ids) {
        if (CollectionUtils.isNotEmpty(ids)) {
            //1.删除用户-角色中间表，角色对应的数据
            //获取角色对应的用户数据
            List<SysUserRole> userRoleList = userRoleService.getListByRoleIds(ids);
            //若该角色绑定了用户的，则不能删除，只有没有绑定用户的角色才能删除，
            Assert.isTrue(CollectionUtils.isEmpty(userRoleList), "删除的角色中已绑定用户，不能删除！");

            //2.删除角色-资源中间表，角色对应的数据
            //获取角色对应的资源数据的主键id
            List<SysRoleResource> roleResourceList = roleResourceService.getListByRoleIds(ids);
            if (CollectionUtils.isNotEmpty(roleResourceList)) {
                List<String> roleResourceIdList = roleResourceList.stream().map(e -> e.getId()).collect(Collectors.toList());
                roleResourceMapper.deleteBatchIds(roleResourceIdList);
            }
            //3.删除角色对应的菜单权限
            roleMenuService.deleteByRoleIds(ids.stream().collect(Collectors.toList()));
            //3.删除角色
            sysRoleMapper.deleteBatchIds(ids);
        }
    }

    @Transactional(readOnly = true)
    @Override
    public SysRoleVO get(String id) {
        Assert.hasText(id, "id不能为空");
        SysRole entity = sysRoleMapper.selectById(id);
        Assert.notNull(entity, "找不到id为 " + id + " 的记录");
        SysRoleVO vo = this.transferVo(entity);
        //获取角色对应的菜单id
        if (ConstantSystem.ADMIN.equals(vo.getCode()) || ConstantSystem.SUPER_ADMIN.equals(vo.getCode())) {
            List<SysMenu> menuList = menuMapper.selectList(null);
            if (CollUtil.isNotEmpty(menuList)) {
                vo.setMenuIds(menuList.stream().map(e -> e.getId()).collect(Collectors.toList()));
            }
        } else {
            List<SysRoleMenu> roleMenuList = roleMenuMapper.selectList(new LambdaQueryWrapper<SysRoleMenu>().eq(SysRoleMenu::getRoleId, id));
            if (CollUtil.isNotEmpty(roleMenuList)) {
                vo.setMenuIds(roleMenuList.stream().map(e -> e.getMenuId()).collect(Collectors.toList()));
            }
        }

        return vo;
    }

    @Transactional(readOnly = true)
    @Override
    public SysRoleVO getDetail(String id) {
        Assert.hasText(id, "id不能为空");
        SysRole entity = sysRoleMapper.selectById(id);
        Assert.notNull(entity, "找不到id为 " + id + " 的记录");
        SysRoleVO vo = this.transferVo(entity);

        List<SysRoleResource> roleResourceList = roleResourceService.getListByRoleId(id);
        if (CollUtil.isNotEmpty(roleResourceList)) {
            vo.setResourceIds(roleResourceList.stream().map(e -> e.getResourceId()).collect(Collectors.toList()));
        }
        return vo;
    }

    @Transactional(readOnly = true)
    @Override
    public SysRole getRoleByCode(String code) {
        QueryWrapper<SysRole> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(SysRole::getStatus, NumberUtils.INTEGER_ZERO)
                .eq(SysRole::getCode, code);
        return sysRoleMapper.selectOne(wrapper);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void distributeRights(RoleRightsDTO dto) {
        Assert.notEmpty(dto.getRights(), "资源数据ID不能为空");
        //验证角色id是否存在
        SysRole entity = sysRoleMapper.selectById(dto.getRoleId());
        Assert.notNull(entity, "角色id不存在");

        //验证资源是存在的
        List<SysResource> resourceList = resourceMapper.selectBatchIds(dto.getRights());
        Assert.notEmpty(resourceList, "资源数据ID不存在！");
        Assert.isTrue(dto.getRights().size() == resourceList.size(), "资源数据id存在错误");

        // 要删除掉该角色已经拥有的资源
        roleResourceMapper.delete(new LambdaQueryWrapper<SysRoleResource>().eq(SysRoleResource::getRoleId, dto.getRoleId()));
        //添加资源
        roleResourceMapper.insertBatch(dto.getRights().stream().map(e -> new SysRoleResource(dto.getRoleId(), e)).collect(Collectors.toList()));
        //给角色分配权限后，需要重新加载资源数据
        dynamicSecurityMetadataSource.clearDataSource();
    }

    @Transactional(readOnly = true)
    @Override
    public void checkRoleDataScope(String roleId) {
        //TODO 验证当前用户是否是管理员
        cn.hutool.core.lang.Assert.notBlank(roleId, "角色id不能为空");
        SysRole roleEntity = sysRoleMapper.selectById(roleId);
        cn.hutool.core.lang.Assert.notNull(roleEntity, "角色id不不存在");
//        if (RoleTypeEnum.COMMON.getKey().equals(roleEntity.getCode())) {
//            throw CustomException.asCustomException("没有权限访问角色数据！");
//        }
    }

    @Transactional(readOnly = true)
    @Override
    public DataStoreDTO<SysRoleVO> selectRolesByUserId(Pageable pageable, String userId) {
        SysUser user = userMapper.selectByUserId(userId);
        cn.hutool.core.lang.Assert.notNull(user, "账号：" + userId + "不存在");
        //根据条件分页查询,获取该用户没有的角色列表
        IPage<SysRoleVO> result = this.sysRoleMapper.selectUnallocatedRolePageByUserId(PageUtils.transferPage(pageable), userId);
        return new DataStoreDTO(result.getTotal(), result.getRecords());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateStatusById(String roleId) {
        SysRole entity = sysRoleMapper.selectById(roleId);
        Assert.notNull(entity, "角色id: " + roleId + "不存在");
        if (NumberUtils.INTEGER_ZERO.equals(entity.getStatus())) {
            entity.setStatus(NumberUtils.INTEGER_ONE);
        } else {
            entity.setStatus(NumberUtils.INTEGER_ZERO);
        }
        sysRoleMapper.updateById(entity);
    }

    private List<SysRole> validateRepeat(String name, String code) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(StringUtils.isNotBlank(code), SysRole::getCode, code)
                .eq(StringUtils.isNotBlank(name), SysRole::getName, name);
        return sysRoleMapper.selectList(queryWrapper);
    }

    private SysRole checkByCode(String code) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(SysRole::getCode, code);
        return sysRoleMapper.selectOne(queryWrapper);
    }

    private SysRole checkByName(String name) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper();
        queryWrapper.lambda().eq(SysRole::getName, name);
        return sysRoleMapper.selectOne(queryWrapper);
    }

    private QueryWrapper<SysRole> buildQuery(SysRoleQueryDTO queryDto) {
        QueryWrapper<SysRole> queryWrapper = new QueryWrapper<>();
        if (Objects.nonNull(queryDto)) {
            queryWrapper.lambda().like(StringUtils.isNotBlank(queryDto.getName()), SysRole::getName, queryDto.getName())
                    .eq(StringUtils.isNotBlank(queryDto.getCode()), SysRole::getCode, queryDto.getCode())
                    .eq(Objects.nonNull(queryDto.getStatus()), SysRole::getStatus, queryDto.getStatus())
                    .ge(ObjUtil.isNotNull(queryDto.getStartCreateTime()), SysRole::getCreateTime, queryDto.getStartCreateTime())
                    .le(ObjUtil.isNotNull(queryDto.getEndCreateTime()), SysRole::getCreateTime, queryDto.getEndCreateTime());
        }
        return queryWrapper;
    }

    private SysRole transferEntity(SysRole entity, SysRoleDTO dto) {
        if (Objects.isNull(entity)) {
            entity = new SysRole();
        }
        BeanUtils.copyProperties(dto, entity);
        return entity;
    }

    private List<SysRoleVO> transferVo(List<SysRole> entities) {
        if (CollectionUtils.isEmpty(entities)) {
            return Lists.newArrayList();
        }

        List<SysRoleVO> voList = entities.stream().map(entity -> {
            SysRoleVO vo = new SysRoleVO();
            BeanUtils.copyProperties(entity, vo);
            return vo;
        }).collect(Collectors.toList());
        return voList;
    }

    private SysRoleVO transferVo(SysRole entity) {
        if (Objects.isNull(entity)) {
            return null;
        }
        SysRoleVO vo = new SysRoleVO();
        BeanUtils.copyProperties(entity, vo);
        return vo;
    }
}
